package com.heima.schedule.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.heima.common.cache.CacheService;
import com.heima.common.constants.ScheduleConstants;
import com.heima.model.schedule.dto.Task;
import com.heima.model.schedule.pojos.Taskinfo;
import com.heima.model.schedule.pojos.TaskinfoLogs;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.List;
import java.util.Set;

@Service
@Slf4j
public class TaskServiceImpl implements TaskService {

    @Autowired
    private TaskinfoService taskinfoService;

    @Autowired
    private TaskinfoLogsService taskinfoLogsService;

    @Autowired
    private CacheService cacheService;

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Long addTask(Task task) {
        log.info("进入添加任务！！！");
        //0 校验task是否为空
        if (task == null) {
            log.warn("入参不能为空");
            return  null;
        }
        //1 插入taskinfo表
        Taskinfo taskinfo = new Taskinfo();
        BeanUtils.copyProperties(task, taskinfo);
        taskinfo.setExecuteTime(new Date(task.getExecuteTime()));

        boolean taskinfoResult = taskinfoService.save(taskinfo);
        if (!taskinfoResult) {
            log.warn("任务插入失败");
            return null;
        }
        //2 插入taskinfoLog表
        TaskinfoLogs logs = new TaskinfoLogs();
        BeanUtils.copyProperties(task, logs);
        logs.setExecuteTime(new Date(task.getExecuteTime()));
        logs.setStatus(0);
        //todo  老师新添加
        logs.setTaskId(taskinfo.getTaskId());

        boolean logsResult = taskinfoLogsService.save(logs);
        if (!logsResult) {
            log.warn("任务插入失败");
            throw new RuntimeException("日志插入失败");
        }
        //todo 为任务设置id
        // 关键 ！！！！为Task赋值id
        task.setTaskId(taskinfo.getTaskId());

        //3 判断任务执行时间是否小于当前时间
        // 任务执行时间
        long executeTime = task.getExecuteTime();
        // 当前时间
        long currentTime = System.currentTimeMillis();
        // 未来五分钟时间
        long futureTime = currentTime + (5 * 60 * 1000);

        // 判断任务执行时间是否小于当前时间
        if (executeTime <= currentTime) {
            // 加入到当前执行队列
            cacheService.lLeftPush("TOPIC", JSON.toJSONString(task));
        }
        // 判断任务执行时间是否小于未来五分钟
        else if (executeTime <= futureTime) {
            cacheService.zAdd("FUTURE", JSON.toJSONString(task), executeTime);
        }
        //5 返回任务id

        return taskinfo.getTaskId();
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean cancelTask(Long taskId) {
        if (taskId == null) {
            log.warn("入参不能为空");
            return false;
        }

        // 1. 删除 taskinfo
        boolean taskInfoResult = taskinfoService.removeById(taskId);
        if (!taskInfoResult) {
            log.warn("任务删除失败");
            return false;
        }

        // 2. 更新 taskinfolog
        LambdaQueryWrapper<TaskinfoLogs> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TaskinfoLogs::getTaskId, taskId);
        TaskinfoLogs logs = taskinfoLogsService.getOne(wrapper);
        logs.setStatus(2);
        boolean logResult = taskinfoLogsService.updateById(logs);
        if (!logResult) {
            log.warn("日志更新失败");
            throw new RuntimeException("日志更新失败");
        }

        // 3. 组装Task结构
        Task task = new Task();
        BeanUtils.copyProperties(logs, task);
        task.setExecuteTime(logs.getExecuteTime().getTime());

        // 4. 删除redis里面的 list 和 zset结构数据
        cacheService.lRemove("TOPIC", 0, JSON.toJSONString(task));
        cacheService.zRemove("FUTURE", JSON.toJSONString(task));

        return true;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Task poll() {
        // // TODO: 2022/7/29   taskJson为null
        String taskJson = cacheService.lRightPop("TOPIC");
        if (StringUtils.isEmpty(taskJson)) {
            return null;
        }

        Task task = JSON.parseObject(taskJson, Task.class);

        // 任务id
        Long taskId = task.getTaskId();

        // 删除任务
        boolean removeResult = taskinfoService.removeById(taskId);
        if (!removeResult) {
            log.warn("任务" + task.getTaskId() + "删除失败");
            return null;
        }


//        long executeTime = task.getExecuteTime();
//        TaskinfoLogs logs = new TaskinfoLogs();
//        BeanUtils.copyProperties(task,logs);
//        logs.setExecuteTime(new Date(executeTime));
//        logs.setStatus(1);

        // 查询任务记录

        TaskinfoLogs taskinfoLogs = taskinfoLogsService.getById(taskId);
        if (taskinfoLogs == null){
            log.warn("taskinfoLogs为空");
            return null;
        }

        // 更新任务记录状态为取消
        taskinfoLogs.setStatus(ScheduleConstants.EXECUTED);


        boolean updateResult = taskinfoLogsService.updateById(taskinfoLogs);
        if (!updateResult) {
            log.warn("任务记录更新失败");
            return null;
        }

        return task;
    }

    /**
     * 定时刷新任务
     */
    @Scheduled(cron = "0 */1 * * * ?")
    public void refresh() {
        //分布式锁的实现
        String token = cacheService.tryLock("FUTURE_TASK_SYNC", 1000 * 30);
        if (StringUtils.isBlank(token)) {
            return;
        }


        System.out.println(System.currentTimeMillis() / 1000 + "执行了定时任务");

        Set<String> tasks = cacheService.zRangeByScore(ScheduleConstants.FUTURE, 0, System.currentTimeMillis());

        if (!tasks.isEmpty()) {
            //将这些任务数据提娜佳到消费者队列中
            cacheService.refreshWithPipeline(ScheduleConstants.FUTURE, ScheduleConstants.TOPIC, tasks);
            System.out.println("成功的将" + ScheduleConstants.FUTURE + "下的当前需要执行的任务数据刷新到" + ScheduleConstants.TOPIC + "下");


        }
    }
    //数据库定时同步到redis
    @Scheduled(cron = "0 */5 * * * ?")
    @PostConstruct//在服务器加载Servlet的时候运行，并且只会被服务器执行一次。PostConstruct在构造函数之后执行，init（）方法之前执行。
    public void initData() {
        //清除缓存
        clear();

        List<Taskinfo> list = taskinfoService.list();
        if (CollectionUtils.isEmpty(list)) {
            log.warn("任务为空");
            return;
        }

        for (Taskinfo taskinfo : list) {
            if (taskinfo == null) {
                continue;
            }

            long executeTime = taskinfo.getExecuteTime().getTime();
            long currentTime = System.currentTimeMillis();
            long futureTime = currentTime + (5 * 60 * 1000);
            Task task = new Task();
            BeanUtils.copyProperties(taskinfo, task);
            task.setExecuteTime(executeTime);

            if (executeTime <= currentTime) {
                cacheService.lLeftPush("TOPIC", JSON.toJSONString(task));

            }

            else if (executeTime <= futureTime) {
                cacheService.zAdd("FUTURE", JSON.toJSONString(task), executeTime);
            }
        }
    }


    private void clear() {
        cacheService.delete("TOPIC");
        cacheService.delete("FUTURE");

    }

}
